// 队列管理器

import { BaseManager } from '../core/BaseManager';
import { RedisClient } from '../core/RedisClient';
import { JobData, Job, QueueConfig, QueueStats, BackoffStrategy } from '../types/queue';
import { KeyGenerator } from '../utils/keyGenerator';
import { v4 as uuidv4 } from 'uuid';

export class QueueManager extends BaseManager {
  private config: QueueConfig;

  constructor(client: RedisClient, config: Partial<QueueConfig> = {}) {
    super(client, new KeyGenerator({ prefix: 'queue' }));

    this.config = {
      defaultJobOptions: {
        priority: 0,
        maxAttempts: 3,
        backoff: { type: 'exponential', delay: 1000 },
      },
      concurrency: 1,
      maxStalledCount: 1,
      stalledInterval: 30000,
      retryProcessDelay: 5000,
      ...config,
    };
  }

  /**
   * 添加作业到队列
   */
  async push(queue: string, item: any): Promise<number> {
    this.validateRequired({ queue, item });

    const queueKey = this.keyGenerator.queue(queue, 'waiting');
    const serializedItem = JSON.stringify(item);

    return this.execute<number>('lpush', queueKey, serializedItem);
  }

  /**
   * 从队列获取作业
   */
  async pop(queue: string): Promise<any> {
    this.validateRequired({ queue });

    const queueKey = this.keyGenerator.queue(queue, 'waiting');
    const item = await this.execute<string | null>('rpop', queueKey);

    return item ? JSON.parse(item) : null;
  }

  /**
   * 查看队列头部作业（不移除）
   */
  async peek(queue: string): Promise<any> {
    this.validateRequired({ queue });

    const queueKey = this.keyGenerator.queue(queue, 'waiting');
    const item = await this.execute<string | null>('lindex', queueKey, -1);

    return item ? JSON.parse(item) : null;
  }

  /**
   * 获取队列长度
   */
  async length(queue: string): Promise<number> {
    this.validateRequired({ queue });

    const queueKey = this.keyGenerator.queue(queue, 'waiting');
    return this.execute<number>('llen', queueKey);
  }

  /**
   * 添加优先级作业
   */
  async pushWithPriority(queue: string, item: any, priority: number): Promise<void> {
    this.validateRequired({ queue, item });
    this.validateNumber(priority, 'priority');

    const queueKey = this.keyGenerator.queue(queue, 'priority');
    const serializedItem = JSON.stringify(item);

    await this.execute('zadd', queueKey, priority, serializedItem);
  }

  /**
   * 获取最高优先级作业
   */
  async popHighestPriority(queue: string): Promise<any> {
    this.validateRequired({ queue });

    const queueKey = this.keyGenerator.queue(queue, 'priority');
    const result = await this.execute<string[]>('zpopmax', queueKey);

    if (result && result.length >= 2) {
      return JSON.parse(result[0]!);
    }

    return null;
  }

  /**
   * 添加延迟作业
   */
  async pushDelayed(queue: string, item: any, delay: number): Promise<void> {
    this.validateRequired({ queue, item });
    this.validateNumber(delay, 'delay', 0);

    const queueKey = this.keyGenerator.queue(queue, 'delayed');
    const processAt = Date.now() + delay * 1000;
    const serializedItem = JSON.stringify(item);

    await this.execute('zadd', queueKey, processAt, serializedItem);
  }

  /**
   * 处理延迟作业（将到期的作业移到等待队列）
   */
  async processDelayed(queue: string): Promise<any[]> {
    this.validateRequired({ queue });

    const delayedKey = this.keyGenerator.queue(queue, 'delayed');
    const waitingKey = this.keyGenerator.queue(queue, 'waiting');
    const now = Date.now();

    // 获取到期的作业
    const expiredJobs = await this.execute<string[]>('zrangebyscore', delayedKey, '-inf', now);

    if (expiredJobs.length === 0) {
      return [];
    }

    // 移动到等待队列
    const pipeline = this.createPipeline();

    for (const job of expiredJobs) {
      pipeline.lpush(waitingKey, job);
      pipeline.zrem(delayedKey, job);
    }

    await pipeline.exec();

    return expiredJobs.map(job => JSON.parse(job));
  }

  /**
   * 添加工作作业
   */
  async addJob(queue: string, jobData: JobData): Promise<string> {
    this.validateRequired({ queue, jobData });

    const jobId = jobData.id ?? uuidv4();
    const job: Job = {
      ...this.config.defaultJobOptions,
      ...jobData,
      id: jobId,
      status: 'waiting',
      createdAt: new Date(),
      processAt: jobData.processAt ?? new Date(),
    };

    const jobKey = this.keyGenerator.job(jobId);

    const startTime = this.now();

    try {
      // 保存作业数据
      await this.setValue(jobKey, job);

      // 添加到队列
      if (job.delay && job.delay > 0) {
        await this.pushDelayed(queue, { jobId }, job.delay);
      } else if (job.priority && job.priority > 0) {
        await this.pushWithPriority(queue, { jobId }, job.priority);
      } else {
        await this.push(queue, { jobId });
      }

      this.logMetric('queue.addJob', this.now() - startTime, true, {
        queue,
        jobId,
        type: job.type,
      });

      return jobId;
    } catch (error) {
      this.logMetric('queue.addJob', this.now() - startTime, false, { queue, error });
      throw this.formatError(error, 'Failed to add job');
    }
  }

  /**
   * 获取作业
   */
  async getJob(queue: string): Promise<Job | null> {
    this.validateRequired({ queue });

    const startTime = this.now();

    try {
      // 首先处理延迟作业
      await this.processDelayed(queue);

      // 尝试从优先级队列获取
      let jobRef = await this.popHighestPriority(queue);

      // 如果优先级队列为空，从普通队列获取
      if (!jobRef) {
        jobRef = await this.pop(queue);
      }

      if (!jobRef?.jobId) {
        return null;
      }

      // 获取完整作业数据
      const jobKey = this.keyGenerator.job(jobRef.jobId);
      const job = await this.getValue<Job>(jobKey);

      if (!job) {
        return null;
      }

      // 更新作业状态
      job.status = 'active';
      job.startedAt = new Date();
      await this.setValue(jobKey, job);

      // 移动到活跃队列
      const activeKey = this.keyGenerator.queue(queue, 'active');
      await this.execute('sadd', activeKey, job.id);

      this.logMetric('queue.getJob', this.now() - startTime, true, {
        queue,
        jobId: job.id,
      });

      return job;
    } catch (error) {
      this.logMetric('queue.getJob', this.now() - startTime, false, { queue, error });
      throw this.formatError(error, 'Failed to get job');
    }
  }

  /**
   * 完成作业
   */
  async completeJob(jobId: string, result?: any): Promise<void> {
    this.validateRequired({ jobId });

    const jobKey = this.keyGenerator.job(jobId);
    const job = await this.getValue<Job>(jobKey);

    if (!job) {
      throw new Error('Job not found');
    }

    job.status = 'completed';
    job.completedAt = new Date();
    job.result = result;

    await this.setValue(jobKey, job);

    // 从活跃队列移除，添加到完成队列
    await this.moveJobBetweenQueues(job, 'active', 'completed');
  }

  /**
   * 作业失败
   */
  async failJob(jobId: string, error: string): Promise<void> {
    this.validateRequired({ jobId, error });

    const jobKey = this.keyGenerator.job(jobId);
    const job = await this.getValue<Job>(jobKey);

    if (!job) {
      throw new Error('Job not found');
    }

    job.attempts = (job.attempts ?? 0) + 1;
    job.error = error;
    job.failedAt = new Date();

    // 检查是否需要重试
    if (job.attempts < (job.maxAttempts ?? 3)) {
      // 计算重试延迟
      const delay = this.calculateBackoffDelay(job.backoff, job.attempts);

      job.status = 'delayed';
      job.processAt = new Date(Date.now() + delay);

      await this.setValue(jobKey, job);
      await this.pushDelayed('retry', { jobId }, delay / 1000);
      await this.moveJobBetweenQueues(job, 'active', 'delayed');
    } else {
      job.status = 'failed';
      await this.setValue(jobKey, job);
      await this.moveJobBetweenQueues(job, 'active', 'failed');
    }
  }

  /**
   * 获取队列统计信息
   */
  async getStats(queue: string): Promise<QueueStats> {
    this.validateRequired({ queue });

    const [waiting, active, completed, failed, delayed] = await Promise.all([
      this.execute<number>('llen', this.keyGenerator.queue(queue, 'waiting')),
      this.execute<number>('scard', this.keyGenerator.queue(queue, 'active')),
      this.execute<number>('scard', this.keyGenerator.queue(queue, 'completed')),
      this.execute<number>('scard', this.keyGenerator.queue(queue, 'failed')),
      this.execute<number>('zcard', this.keyGenerator.queue(queue, 'delayed')),
    ]);

    const total = waiting + active + completed + failed + delayed;

    return {
      waiting,
      active,
      completed,
      failed,
      delayed,
      paused: 0, // 简化实现
      total,
      throughput: {
        completed: completed,
        failed: failed,
        perMinute: 0, // 需要时间窗口统计
        perHour: 0,
      },
    };
  }

  /**
   * 清空队列
   */
  async clear(queue: string): Promise<void> {
    this.validateRequired({ queue });

    const keys = [
      this.keyGenerator.queue(queue, 'waiting'),
      this.keyGenerator.queue(queue, 'active'),
      this.keyGenerator.queue(queue, 'completed'),
      this.keyGenerator.queue(queue, 'failed'),
      this.keyGenerator.queue(queue, 'delayed'),
      this.keyGenerator.queue(queue, 'priority'),
    ];

    await this.deleteKeys(...keys);
  }

  /**
   * 在队列之间移动作业
   */
  private async moveJobBetweenQueues(job: Job, fromType: string, toType: string): Promise<void> {
    const fromKey = this.keyGenerator.generate('queue', 'default', fromType);
    const toKey = this.keyGenerator.generate('queue', 'default', toType);

    const pipeline = this.createPipeline();
    pipeline.srem(fromKey, job.id);
    pipeline.sadd(toKey, job.id);
    await pipeline.exec();
  }

  /**
   * 计算退避延迟
   */
  private calculateBackoffDelay(backoff?: BackoffStrategy, attempt: number = 1): number {
    if (!backoff) {
      return 1000; // 默认1秒
    }

    let delay = backoff.delay;

    switch (backoff.type) {
      case 'exponential':
        delay = backoff.delay * Math.pow(backoff.factor ?? 2, attempt - 1);
        break;
      case 'linear':
        delay = backoff.delay * attempt;
        break;
      case 'fixed':
      default:
        delay = backoff.delay;
        break;
    }

    return Math.min(delay, backoff.maxDelay ?? 60000);
  }
}
